package exam;

import java.util.*;

/**
 * Provides methods for checking the results of questions.
 * 
 * @author Branden Ogata
 *
 */

public class Tester
{
  /**
   * The total number of points obtained for a question.
   * 
   */
  protected static double totalPoints = 0;

  /**
   * This and all of the other static variables below provide consistent values for points.
   * 
   */
  protected static final double HALF = 1.0 / 2.0;
  protected static final double THIRD = 1.0 / 3.0;
  protected static final double FOURTH = 1.0 / 4.0;
  protected static final double FIFTH = 1.0 / 5.0;
  protected static final double SIXTH = 1.0 / 6.0;
  protected static final double SEVENTH = 1.0 / 7.0;
  protected static final double EIGHTH = 1.0 / 8.0;
  protected static final double NINTH = 1.0 / 9.0;
  protected static final double TENTH = 1.0 / 10.0;
  protected static final double ELEVENTH = 1.0 / 11.0;
  protected static final double TWELFTH = 1.0 / 12.0;
  protected static final double THIRTEENTH = 1.0 / 13.0;
  protected static final double FOURTEENTH = 1.0 / 14.0;
  protected static final double FIFTEENTH = 1.0 / 15.0;
  protected static final double SIXTEENTH = 1.0 / 16.0;
  
  
  /**
   * Returns a String describing the results of a comparison between two items.
   * 
   * @param type The Category indicating the type of test to perform.
   * @param msg The String containing the initial message to print.
   * @param first An Object containing the expected result for the test.
   * @param second An Object containing the actual result for the test.
   * @param points The double containing the number of points for the test.
   * 
   * @return result A String containing the result from the test and the points awarded.
   * 
   */
  
  public static String test(Category type, String msg, Object first, Object second, double points)
  {
    String results = "";
    
    switch (type)
    {
      case EQUALS:
        results += Tester.assertEquals(msg, first, second);
        break;
      case NOT_EQUALS:
        results += Tester.assertNotEquals(msg, first, second);
        break;
      case EQUALS_REGEX:
        results += Tester.assertRegex(msg, (String) first, (String) second);
        break;
      case IS_NULL:
        results += Tester.assertNull(msg, second);
        break;
      default:
        break;
    }
    
    double awarded = 0;
    
    // If the test passed, then give full points; else give no points
    // (not yet certain how to handle partial credit for individual tests)
    if (results.endsWith("PASS"))
    {
      awarded = points;
      addTotalPoints(awarded);
    }

    results += String.format(" [%.2f/%.2f points]", awarded, points);
    
    return results; 
  }
  
  /**
   * Indicates whether two objects are equal to one another.
   * 
   * @param msg The String containing the initial message to print.
   * @param first An Object containing the expected result for the test.
   * @param second An Object containing the actual result for the test.
   * 
   * @return A String that is "PASS" if the objects are equal,
   *                          "FAIL" otherwise.
   * 
   */
  
  public static String assertEquals(String msg, Object first, Object second)
  {
    return String.format("%s %s", msg, ((first.equals(second)) ? ("PASS") : ("expected " + first + 
                                                                             " but was " + second + 
                                                                             " FAIL")));
  }  
  
  /**
   * Indicates whether two objects are not equal to one another.
   * 
   * @param msg The String containing the initial message to print.
   * @param first An Object containing the expected result for the test.
   * @param second An Object containing the actual result for the test.
   * 
   * @return A String that is "PASS" if the objects are not equal,
   *                          "FAIL" otherwise.
   * 
   */
  
  public static String assertNotEquals(String msg, Object first, Object second)
  {
    return String.format("%s %s", msg, ((!first.equals(second)) ? ("PASS") : ("expected " + first + 
                                                                              " and was " + second + 
                                                                              " FAIL")));
  }  
  
  /**
   * Indicates whether two objects are equal in regular expressions to one another.
   * 
   * @param msg The String containing the initial message to print.
   * @param regex An Object containing the regex for the test.
   * @param text An Object containing the actual result for the test.
   * 
   * @return A String that is "PASS" if the objects are equal according to regex,
   *                          "FAIL" otherwise.
   * 
   */
  
  public static String assertRegex(String msg, String regex, String text)
  {
    return String.format("%s %s", msg, ((text.matches(regex)) ? ("PASS") : ("expected " + regex + 
                                                                            " but was " + text + 
                                                                            " FAIL")));
  }    

  /**
   * Indicates whether an object is null.
   * 
   * @param msg The String containing the initial message to print.
   * @param value An Object containing the value for the test.
   * 
   * @return A String that is "PASS" if the objects are equal according to regex,
   *                          "FAIL" otherwise.
   * 
   */
  
  public static String assertNull(String msg, Object value)
  {
    return String.format("%s %s", msg, ((value == null) ? ("PASS") : ("expected" + null + 
                                                                      " but was " + value + 
                                                                      " FAIL")));
  }    
  
  
  /**
   * Calculates the number of points to award given a percentage of some maximum points.
   * 
   * @param percentage The double equal to the percentage of the points to get.
   * @param maxPoints The double equal to the number of points available.
   * 
   * @param A double equal to the number of points that make up the given percentage of points.
   * 
   */
  
  public static double percentageToPoints(double percentage, double maxPoints)
  {
    return percentage * maxPoints;
  }
  
  /**
   * Returns the number of points from the first index of the given String array.
   * This is used in extracting a command line argument.
   * 
   * @param args The String[] that should contain the number of points for the question.
   * 
   * @return points A double equal to the number given in the first element of args.
   *                If no such element exists or the element is not a number, then this is 1.
   *                
   */
  
  protected static double getPoints(String[] args)
  {
    double points;
    
    try
    {
      points = Double.valueOf(args[0]);
    }
    catch (NumberFormatException e)
    {
      points = 1;
    }
    catch (IndexOutOfBoundsException e)
    {
      points = 1;
    }
    
    return points;
  }
  
  /**
   * Gets the total number of points obtained thus far.
   * 
   * @return this.totalPoints The double equal to the number of points for the user.
   * 
   */
  
  public static double getTotalPoints()
  {
    return totalPoints;
  }
  
  /**
   * Updates the total number of points obtained thus far.
   * 
   * @param points The double equal to the number of points to add to the total.
   * 
   */
  
  public static void addTotalPoints(double points)
  {
    totalPoints += points;
  }
}
